package com.springsource.boot.construct;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;

import java.lang.reflect.Method;
import java.util.Set;

@Configuration
@Slf4j
public class SpringbootConstruct {

    public static void main(String[] args) throws Exception {

        log.warn("1. 演示获取 Bean Definition 源");
        SpringApplication spring = new SpringApplication(SpringbootConstruct.class);
        spring.setSources(Set.of("classpath:b01.xml"));

        log.warn("2. 演示推断应用类型");
        // SpringApplication - this.webApplicationType = WebApplicationType.deduceFromClasspath()
        Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
        deduceFromClasspath.setAccessible(true);
        WebApplicationType type = (WebApplicationType) deduceFromClasspath.invoke(null);
        log.error("推断应用类型：{}", type);

        log.warn("3. 演示 ApplicationContext 初始化器");
        // 在创建ApplicationContext之后，refresh之前，对ApplicationContext做扩展
        spring.addInitializers(context -> {
            if (context instanceof GenericApplicationContext gac) {
                gac.registerBean(Bean3.class);
            }
        });

        log.warn("4. 演示监听器与事件");
        spring.addListeners(event -> {
            System.out.println("事件为：" + event.getClass());
        });

        log.warn("5. 演示主类推断");
        Method deduceMainApplicationClass = SpringApplication.class.getDeclaredMethod("deduceMainApplicationClass");
        deduceMainApplicationClass.setAccessible(true);
        Class<?> mainClass = (Class<?>) deduceMainApplicationClass.invoke(spring);
        log.error("主类：{}", mainClass);

        ConfigurableApplicationContext context = spring.run(args);
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {

            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);
            log.error("名称：{}:", beanDefinitionName);
            String description = beanDefinition.getResourceDescription();
            log.error("来源：{}:", description);
        }

        context.close();
    }


    static class Bean1 {}

    static class Bean2 {}

    static class Bean3 {}

    @Bean
    public Bean2 bean2() {
        return new Bean2();
    }

    @Bean
    public TomcatServletWebServerFactory  tomcatServletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }

}
